﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Library.OrmEnterpriseEntity.Factor;

namespace Library.OrmEnterpriseEntity.TableInfo {
	///<summary>
	///</summary>
	public abstract class DbTable : ITable, INotifyPropertyChanged, INotifyPropertyChanging, ISupportInitialize {
		protected readonly DbTableView DbTableMetadata;
		protected internal bool IsInitialize { get; private set; }
		///<summary>
		///</summary>
		public const string StatePropertyName = "State";
		///<summary>
		///</summary>
		public const string MarkDeletePropertyName = "MarkDelete";
		private static readonly Dictionary<string, PropertyChangedEventArgs> PropertyChangedDic =
			new Dictionary<string, PropertyChangedEventArgs>();

		private static readonly Dictionary<string, PropertyChangingEventArgs> PropertyChangingDic =
			new Dictionary<string, PropertyChangingEventArgs>();

		protected DbTable(DbTableView metadata) {
			DbTableMetadata = metadata;
			_state = EntityState.New;
			Events = new EventHandlerList();
		}

		private ColumnCollection _columns;

		public virtual string TableName {
			get { return DbTableMetadata.TableName; }

		}

		protected bool IsMarkDelete;
		///<summary>
		/// Mark entity for deleted
		///</summary>
		public bool MarkDelete {
			get { return IsMarkDelete; }
			set {
				if (value != IsMarkDelete) {
					OnPropertyChanging(MarkDeletePropertyName);
					IsMarkDelete = value;
					OnPropertyChanged(MarkDeletePropertyName);
				}
			}
		}

		///<summary>
		///</summary>
		public virtual ColumnCollection Columns {
			get { return _columns ?? (_columns = new ColumnCollection()); }
		}

		///<summary>
		///</summary>
		///<returns></returns>
		public DbTableView GetMetadata() {
			return DbTableMetadata;
		}



		private EntityState _state;
		///<summary>
		/// 
		///</summary>
		public EntityState State {
			get { return _state; }
			set {
				if (_state != value) {
					OnPropertyChanging(StatePropertyName);
					var old = _state;
					_state = value;
					OnPropertyChanged(StatePropertyName);
					OnStateChanged(new StateChangedEventArgs(old, _state));
				}
			}
		}

		/// <summary>
		/// check column has default value or not
		/// </summary>
		/// <exception cref="ValidattionException"></exception>
		public virtual void Validate() {
			foreach (DbColumn column in Columns) {
				if (!column.Validate()) {
					var e = new ThrowValidationExceptionEventArgs(column, ValidationError.Validation);
					OnThrowValidation(e);
					string error = e.Message;
					if (String.IsNullOrEmpty(error)) error = DbContext.Instance.Context.StringResource.ColumnValidateFail(column, e.ColumnName, e.ColumnValue, e.TableName);
					throw new ValidattionException(error, column);
				}
			}
		}

		protected virtual string LogNew() {
			var log = new StringBuilder();
			log.Append("INSERT FROM ");
			log.Append(TableName);
			log.Append(" WHERE ");
			foreach (DbColumn column in Columns) {
				if (column.ValueDbChange) {
					log.AppendFormat("{0}={1}.", column.ColumnName, column.DbValue);
				}
			}
			return log.ToString();
		}
		/// <summary>
		/// Log entity
		/// </summary>
		/// <returns></returns>
		public string Log() {
			switch (State) {
				case EntityState.New:
				case EntityState.Inserting:
					return LogNew();
				case EntityState.DbChange:
				case EntityState.Updating:
					return LogChange();
				case EntityState.Delete:
				case EntityState.Deleting:
					return LogDelete();
				default:
					return String.Empty;
			}
		}
		protected virtual string LogDelete() {
			var log = new StringBuilder();
			log.Append("DELETE FROM ");
			log.Append(TableName);
			log.Append(" WHERE ");
			foreach (DbColumn column in Columns) {
				log.AppendFormat("{0}={1}.", column.ColumnName, column.DbValue);
			}
			return log.ToString();
		}
		protected virtual string LogChange() {
			bool hasChanged = false;

			var log = new StringBuilder();
			log.Append("UPDATE FROM ");
			log.Append(TableName);
			log.Append(" WHERE ");
			foreach (DbColumn column in Columns) {
				if (column.ValueDbChange) {
					hasChanged = true;
					log.AppendFormat("{0}: Old={1}.New={2}.", column.ColumnName, column.Original, column.DbValue);
				}
			}
			if (hasChanged)
				return log.ToString();
			return String.Empty;
		}

		internal delegate void StateChangingdEventHandler(object sender, EntityState oldState, EntityState newState);

		///<summary>
		/// Fires immediately after the state value has been changed 
		///</summary>
		public event EventHandler<StateChangedEventArgs> StateChanged {
			add {
				Events.AddHandler(EventStateChanged, value);
			}
			remove {
				Events.RemoveHandler(EventStateChanged, value);
			}
		}
		protected virtual void OnStateChanged(StateChangedEventArgs e) {
			if (IsInitialize) return;
			var handler = (EventHandler<StateChangedEventArgs>)Events[EventStateChanged];
			if (handler != null) handler(this, e);
		}

		protected internal virtual void OnThrowValidation(ThrowValidationExceptionEventArgs e) {
			var handle = (EventHandler<ThrowValidationExceptionEventArgs>)Events[EventThrowValidation];
			if (handle != null) {
				handle(this, e);
			}
		}

		protected static readonly object EventPropertyChanged = new object();

		protected static readonly object EventPropertyChanging = new object();

		protected static readonly object EventStateChanged = new object();

		protected static readonly object EventThrowValidation = new object();

		protected EventHandlerList Events { get; private set; }

		protected internal virtual void OnPropertyChanged(string propertyName) {
			if (IsInitialize) return;
			if (String.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
			PropertyChangedEventArgs e;
			lock (PropertyChangedDic) {
				if (!PropertyChangedDic.TryGetValue(propertyName, out  e)) {
					e = new PropertyChangedEventArgs(propertyName);
					PropertyChangedDic.Add(propertyName, e);
				}
			}
			OnPropertyChanged(e);
		}

		private void OnPropertyChanged(PropertyChangedEventArgs e) {
			if (IsInitialize) return;
			var handler = (PropertyChangedEventHandler)Events[EventPropertyChanged];
			if (handler != null) {
				handler(this, e);
			}
		}

		protected internal virtual void OnPropertyChanging(string propertyName) {
			if (IsInitialize) return;
			if (String.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
			PropertyChangingEventArgs e;
			lock (PropertyChangingDic) {
				if (!PropertyChangingDic.TryGetValue(propertyName, out  e)) {
					e = new PropertyChangingEventArgs(propertyName);
					PropertyChangingDic.Add(propertyName, e);
				}
			}
			OnPropertyChanging(e);
		}

		private void OnPropertyChanging(PropertyChangingEventArgs e) {
			if (IsInitialize) return;
			var handler = (PropertyChangingEventHandler)Events[EventPropertyChanging];
			if (handler != null) {
				handler(this, e);
			}
		}
		/// <summary>
		/// Fires immediately after the PropertyChanged value has been changed 
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged {
			add { Events.AddHandler(EventPropertyChanged, value); }
			remove { Events.RemoveHandler(EventPropertyChanged, value); }
		}
		/// <summary>
		/// Fires when an end-user starts to modify the property value by editor
		/// </summary>
		public event PropertyChangingEventHandler PropertyChanging {
			add { Events.AddHandler(EventPropertyChanging, value); }
			remove { Events.RemoveHandler(EventPropertyChanging, value); }
		}
		/// <summary>
		/// Fires immediately after Validator throw an exception
		/// </summary>
		public event EventHandler<ThrowValidationExceptionEventArgs> ThrowValidation {
			add { Events.AddHandler(EventThrowValidation, value); }
			remove { Events.RemoveHandler(EventThrowValidation, value); }
		}

		public void BeginInit() {
			IsInitialize = true;
		}

		public void EndInit() {
			IsInitialize = false;
		}
		internal static readonly DbTable Null = new MetadataTable(null);
		private class MetadataTable : DbTable {

			public override bool Equals(object obj) {
				return ReferenceEquals(this, obj);
			}

			protected internal override void OnPropertyChanged(string propertyName) {

			}
			protected internal override void OnPropertyChanging(string propertyName) {

			}
			protected override void OnStateChanged(StateChangedEventArgs e) {

			}
			protected internal override void OnThrowValidation(ThrowValidationExceptionEventArgs e) {

			}
			public override string TableName {
				get { return "MetadataTable"; }
			}
			public override void Validate() {

			}
			public MetadataTable(DbTableView metadata)
				: base(metadata) {
			}

			public override ColumnCollection Columns {
				get {
					return new ColumnList();
				}
			}

			//internal static readonly DbTable NullTable = new MetadataTable(null);

			private class ColumnList : ColumnCollection {
				#region Implementation of IEnumerable


				#endregion

				#region Implementation of ICollection<DbColumn>

				public override void Add(DbColumn item) {
				}

				public override void Clear() {
				}

				public override bool Contains(DbColumn item) {
					return false;
				}

				public override void CopyTo(DbColumn[] array, int arrayIndex) {

				}

				public override bool Remove(DbColumn item) {
					return false;
				}

				public override int Count {
					get { return 0; }
				}

				public override bool IsReadOnly {
					get { return true; }
				}

				#endregion

				#region Implementation of IList<DbColumn>

				public override int IndexOf(DbColumn item) {
					return -1;
				}

				public override void Insert(int index, DbColumn item) {
				}

				public override void RemoveAt(int index) {
				}

				public override DbColumn this[int index] {
					get { return null; }
					set { }
				}

				#endregion
			}

		}
	}
}